﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;

namespace Client {
	public enum LoggerStates {
		Log,
		Warning,
		Error,
		Critical,
		Info,
		Debug,
		Test
	}

	internal struct LoggerMessage {
		public String Message {
			get;
			set;
		}
		public LoggerStates State {
			get;
			set;
		}
		public DateTime Time {
			get;
			set;
		}
	}

	public static class Logger {
		private static RichTextBox richtextbox;
		private static int removedLines = 0;
		private static List<LoggerMessage> buffer = new List<LoggerMessage>();
		private static List<String> logs = new List<string>();

		public static void setTextBox(RichTextBox richtextbox) {
			Logger.richtextbox = richtextbox;
		}

		public static void Write(String text) {
			Write(text, LoggerStates.Log);
		}

		public static void Log(String text) {
			Write(text, LoggerStates.Log);
		}

		public static void Critical(String text) {
			Write(text, LoggerStates.Critical);
		}

		public static void Debug(String text) {
			Write(text, LoggerStates.Debug);
		}

		public static void Error(String text) {
			Write(text, LoggerStates.Error);
		}

		public static void Info(String text) {
			Write(text, LoggerStates.Info);
		}

		public static void Warning(String text) {
			Write(text, LoggerStates.Warning);
		}

		public static void Test(String text) {
			Write(text, LoggerStates.Test);
		}

		public static String[] GetLogs(int from, int size) {
			String[] Lines = new String[0];
			lock (logs) {
				int maxSize = logs.Count;
				if (size <= 0) {
					size = maxSize;
				}
				if (maxSize - from < size) {
					size = maxSize - from;
				}

				Lines = new String[size];
				Array.Copy(logs.ToArray(), from, Lines, 0, size);
			}
			return Lines;
		}

		public static String[] GetLogs(int from) {
			// collect from chosen index
			return GetLogs(from, 0);
		}

		public static String[] GetLogs() {
			// collect from the beginning
			return GetLogs(0);
		}

		delegate void RichTextBoxInvokeDelegate();

		public static void Write(String text, LoggerStates state) {
			Write(text, state, DateTime.Now);
		}

		public static void Write(String text, LoggerStates state, DateTime now) {
			LoggerMessage message = new LoggerMessage() {
				State = state,
				Message = text,
				Time = now
			};

			lock (logs) {
				logs.Add(
					now.ToString("[dd\\/MM\\/yyyy HH:mm:ss.fffff::") +
					String.Format("{0}] {1}", state, text)
				);
			}

			if (null == text) {
				text = "[null]";
			}

			if (null == richtextbox) {
				lock (buffer) {
					buffer.Add(message);
				}
				return;
			}

			// read from buffer
			lock (buffer) {
				if (buffer.Count > 0) {
					LoggerMessage bufferedMessage = buffer[buffer.Count - 1];
					buffer.Remove(bufferedMessage);
					Write(bufferedMessage.Message, bufferedMessage.State, bufferedMessage.Time);
				}
			}

			try {
				richtextbox.BeginInvoke(new RichTextBoxInvokeDelegate(() => {
					Font font = richtextbox.SelectionFont;
					Font boldFont = new Font(font, FontStyle.Bold);
					Font italicFont = new Font(font, FontStyle.Italic);

					richtextbox.SelectionColor = Color.Black;
					richtextbox.AppendText("[");
					richtextbox.SelectionColor = Color.LightGray;
					richtextbox.AppendText(now.ToString("dd\\/MM\\/yyyy"));
					richtextbox.SelectionColor = Color.Gray;
					richtextbox.AppendText(now.ToString(" HH:mm:ss.fffff"));
					richtextbox.SelectionColor = Color.Black;
					richtextbox.AppendText("::");

					switch (state) {
						case LoggerStates.Log:
							richtextbox.AppendText("Log] ");
							break;
						default:
							break;
						case LoggerStates.Critical:
							richtextbox.AppendText("Critical] ");
							richtextbox.SelectionFont = boldFont;
							richtextbox.SelectionColor = Color.Red;
							break;
						case LoggerStates.Debug:
							richtextbox.AppendText("Debug] ");
							richtextbox.SelectionColor = Color.Gray;
							richtextbox.SelectionFont = italicFont;
							break;
						case LoggerStates.Error:
							richtextbox.AppendText("Error] ");
							richtextbox.SelectionFont = boldFont;
							richtextbox.SelectionColor = Color.DarkRed;
							break;
						case LoggerStates.Info:
							richtextbox.AppendText("Info] ");
							richtextbox.SelectionColor = Color.CadetBlue;
							break;
						case LoggerStates.Warning:
							richtextbox.AppendText("Warning] ");
							richtextbox.SelectionColor = Color.OrangeRed;
							break;
						case LoggerStates.Test:
							richtextbox.AppendText("Test] ");
							richtextbox.SelectionColor = Color.RoyalBlue;
							break;
					}
					richtextbox.AppendText(text);
					richtextbox.SelectionFont = font;

					richtextbox.AppendText("\n");

					int stringLineLength = 0;
					int linesToRemove = richtextbox.Lines.Length - 16384;
					if (linesToRemove > 0) {
						removedLines += linesToRemove;
						for (int i = 0; i < linesToRemove; ++i) {
							stringLineLength += richtextbox.Lines[i].Length + 1;
						}
						richtextbox.SelectionStart = 0;
						richtextbox.SelectionLength = stringLineLength;
						richtextbox.SelectedText = "(...)\n";
					}

					richtextbox.SelectionStart = richtextbox.TextLength;
					richtextbox.DeselectAll();

					richtextbox.ScrollToCaret();
				}));
			} catch (NullReferenceException) {
				lock (buffer) {
					buffer.Add(message);
				}
			} catch (InvalidOperationException) {
				lock (buffer) {
					buffer.Add(message);
				}
			}
		}
	}
}